﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMeterParamFrame
{
    public class MeterControl
    {
        public delegate bool Func_SendDataToSet(ref byte[] BytSend, bool IsReturn, int Bwh);
        /// <summary>
        /// 身份认证委托
        /// </summary>
        /// <param name="ks"></param>
        /// <param name="meterID"></param>
        /// <param name="outRand"></param>
        /// <param name="outEndata"></param>
        /// <returns></returns>
        public delegate bool Func_IdentityAuthentication(EnumKeyState ks, string meterID, out string outRand, out string outEndata);
        /// <summary>
        /// 清理指令密文获取
        /// </summary>
        /// <param name="KeyState"></param>
        /// <param name="meterID"></param>
        /// <param name="MeterRand"></param>
        /// <param name="MeterEsamSerial"></param>
        /// <param name="PutKey"></param>
        /// <param name="OutKey"></param>
        /// <param name="OutMac"></param>
        /// <returns></returns>
        public delegate bool Func_GetClearKey(EnumKeyState KeyState, string meterID, string MeterRand, string MeterEsamSerial, string PutKey, out string OutKey, out string OutMac);
        /// <summary>
        /// 密钥下装时密钥获取回调函数
        /// </summary>
        /// <param name="meterID"></param>
        /// <param name="MeterRand"></param>
        /// <param name="MeterEsamSerial"></param>
        /// <param name="OutData"></param>
        /// <returns></returns>
        public delegate bool Func_GetUpdateKey(string meterID, string MeterRand, string MeterEsamSerial, out Dictionary<EnumKeyNo, string> OutData);

        /// <summary>
        /// 费控功能指令密文获取回调函数
        /// </summary>
        /// <param name="Uc"></param>
        /// <param name="MeterDate"></param>
        /// <param name="MeterRand"></param>
        /// <param name="MeterEsamSerial"></param>
        /// <param name="OutData"></param>
        /// <returns></returns>
        public delegate bool Func_GetUserControl(EnumKeyState ks, EnumUserControl Uc, DateTime MeterDate, string MeterRand, string MeterEsamSerial, out string OutData);

        /// <summary>
        /// 一类参数MAC获取回调函数
        /// </summary><param name="ks"></param>
        /// <param name="MeterRand"></param>
        /// <param name="Meterid"></param>
        /// <param name="MeterEsamSerial"></param>
        /// <param name="PutApdu"></param>
        /// <param name="PutData"></param>
        /// <param name="OutEndata"></param>
        /// <returns></returns>
        public delegate bool Func_GetParameterUpdate(EnumKeyState ks, string MeterRand, string Meterid, string MeterEsamSerial, string PutApdu, string PutData, out string OutEndata);
        /// <summary>
        /// 二类参数密文和MAC获取回调函数
        /// </summary>
        /// <param name="ks"></param>
        /// <param name="MeterRand"></param>
        /// <param name="Meterid"></param>
        /// <param name="MeterEsamSerial"></param>
        /// <param name="PutApdu"></param>
        /// <param name="PutData"></param>
        /// <param name="OutEndata"></param>
        /// <param name="OutMac"></param>
        /// <returns></returns>
        public delegate bool Func_GetParameterElseUpdate(EnumKeyState ks, string MeterRand, string Meterid, string MeterEsamSerial, string PutApdu, string PutData, out string OutEndata, out string OutMac);
        /// <summary>
        /// 重试次数
        /// </summary>
        private const int CONST_RECOUNT = 2;

        /// <summary>
        /// 身份认证次数
        /// </summary>
        private int IdentityNum = 3;

        /// <summary>
        /// 
        /// 是否打印Debug信息
        /// </summary>
        private static bool m_IsPrintDebug = false;

        public static string CONST_DEBUGPATH
        { get { return "Debug"; } }

        private Model.MeterInfo meterInfo = null;
        /// <summary>
        /// 是否已经读取到表号的标志
        /// </summary>
        private bool isReadID = false;
        /// <summary>
        /// 表号
        /// </summary>
        private string meterID = "";
        ///// <summary>
        ///// 内部版本
        ///// </summary>
        //private int verNo = 0;
        /// <summary>
        /// 随机数
        /// </summary>
        private string rand = "";
        /// <summary>
        /// ESAM序列号
        /// </summary>
        private string esamSerial = "";
        /// <summary>
        /// 密钥状态标志，是否在私钥下
        /// </summary>
        private bool isPrivateKey = false;
        /// <summary>
        /// 是否做过清零操作
        /// </summary>
        private bool isClear;
        /// <summary>
        /// 第一步是否完成
        /// </summary>
        private bool firstOK = false;
        /// <summary>
        /// 获取是否需要插卡
        /// </summary>
        public bool IsInCode
        {
            get;
            private set;
        }
        /// <summary>
        /// 是否允许打印Debug语句
        /// </summary>
        public static bool IsPrintDebug
        {
            set { m_IsPrintDebug = value; }
        }

        /// <summary>
        /// 在更新界面时，是否使用异步调用
        /// </summary>
        public static bool IsAsynchronous
        {
            get;
            set;
        }

        /// <summary>
        /// 是否是远程费控
        /// </summary>
        public bool IsYuanCheng = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="Item"></param>
        public MeterControl(Model.MeterInfo Item)
        {
            meterInfo = Item;
            isReadID = false;
        }

        #region ---------------外部公开函数------------------

        #region ---------------------读表地址---------------------
        /// <summary>
        /// 读取表地址
        /// </summary>
        /// <param name="sendData">数据发送回调函数</param>
        /// <returns></returns>
        public bool ReadMeterAddr(Func_SendDataToSet sendData)
        {
            meterInfo.MeterAddress = "".PadLeft(12, 'A');

            string errString;
            string BwCode;
            string meterAddr;

            byte[] bytSendData = MeterCommProtocol.WeaveDataByGetMeterAddr(out BwCode);

            meterInfo.CurrentDesc = "正在读取表地址";
#if DEBUG
            errString = "";
            meterAddr = new Random().Next(1000000).ToString().PadLeft(12, '0');
            System.Threading.Thread.Sleep(2000);
#else

            if (!SendDataToMeter(sendData, ref bytSendData)) return false;

            if (!MeterCommProtocol.ExplainDataByGetMeterAddr(bytSendData, out meterAddr, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "读取表地址失败，返回帧无法解析:" + errString;
                return false;
            }
#endif
            meterInfo.MeterAddress = meterAddr;

            meterInfo.CurrentDesc = "通信地址读取成功";

            return true;
        }
        #endregion

        #region --------------------------读取表号-------------------------

        public bool ReadMeterID(Func_SendDataToSet sendData)
        {
            if (meterInfo.MeterAddress == "" || meterInfo.MeterAddress == "".PadLeft(12, 'A'))
            {
                if (!ReadMeterAddr(sendData)) return false;
            }

            meterInfo.CurrentDesc = "正在读取电能表表号";

#if DEBUG
            meterID = "122222222";
            System.Threading.Thread.Sleep(2000);
#else
            string BwCode;
            string errString;

            byte[] bytData = MeterCommProtocol.WeaveDataByGetMeterNo(meterInfo.MeterAddress, out BwCode);

            if (!SendDataToMeter(sendData, ref bytData)) return false;

            if (!MeterCommProtocol.ExpalinDataByGetMeterNo(bytData, ref meterID, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "读取电能表表号失败，返回帧无法解析:" + errString;
                return false;
            }

#endif
            isReadID = true;
            return true;
        }


        #endregion

        #region ----------------------身份认证操作-----------------
        /// <summary>
        /// 电能表身份认证
        /// </summary>
        /// <param name="ks">密钥状态</param>
        /// <param name="sendData">发送数据回调函数</param>
        /// <param name="Identity">加密机访问回调函数</param>
        /// <returns></returns>
        public bool IdentityAuthentication(EnumKeyState ks, Func_SendDataToSet sendData, Func_IdentityAuthentication Identity)
        {
            return getMeterESAM(sendData, Identity, ks);
        }

        #endregion

        #region ----------------------密钥下装操作----------------------
        /// <summary>
        /// 密钥下装第一步操作（共2步操作）
        /// </summary>
        /// <returns></returns>
        public bool GotoFirstStep(Func_SendDataToSet sendData, Func_IdentityAuthentication Identity, Func_GetClearKey getClearKey)
        {
            if (!ReadMeterAddrAndID(sendData)) return false;
            isPrivateKey = false;

            if (!getMeterESAM(sendData, Identity)) return false;        //身份认证

            if (!CheckMeterStateInfo(sendData, getClearKey, Identity)) return false;        //状态检查

            if (!isClear && !IsYuanCheng)
            { IsInCode = true; }

            firstOK = true;
            return true;
        }

        /// <summary>
        /// 密钥下装第二部操作
        /// </summary>
        /// <param name="sendData"></param>
        /// <param name="Identity"></param>
        /// <param name="getClearKey"></param>
        /// <param name="getUpdateKey"></param>
        /// <returns></returns>
        public bool GotoSecondStep(Func_SendDataToSet sendData, Func_IdentityAuthentication Identity, Func_GetClearKey getClearKey, Func_GetUpdateKey getUpdateKey)
        {
            if (!firstOK) return false;

            return DownKeyToMeter(sendData, Identity, getClearKey, getUpdateKey);
        }


        #endregion

        #region -----------------------费控功能控制----------------------

        public bool UserControlVir(EnumUserControl uc, Func_SendDataToSet sendData, Func_IdentityAuthentication Identity, Func_GetUserControl getUserControl)
        {
            if (!ReadMeterAddrAndID(sendData)) return false;

            if (!getMeterESAM(sendData, Identity)) return false;

#if DEBUG
            meterInfo.CurrentDesc = string.Format("正在进行{0}功能操作", uc);
            System.Threading.Thread.Sleep(1000);
            meterInfo.CurrentDesc = string.Format("{0}功能操作成功", uc);
#else
            DateTime controlTime; string outData;

            //if (!getMeterTime(sendData, out controlTime))
            {
                controlTime = DateTime.Now;
            }

            meterInfo.CurrentDesc = string.Format("正在从加密机中获取{0}功能操作密文", uc);

            if (!getUserControl(isPrivateKey ? EnumKeyState.私钥状态 : EnumKeyState.公钥状态, uc, controlTime, this.rand, this.esamSerial, out outData))
            {
                meterInfo.CurrentDesc = string.Format("从加密机获取{0}控制功能操作密文失败", uc);
                return false;
            }

            meterInfo.CurrentDesc = string.Format("正在进行电能表的{0}控制操作", uc);
            string BwCode; string errString;

            byte[] bytData = MeterCommProtocol.WeaveDataByUserControl(meterInfo.MeterAddress, outData, out BwCode);

            if (!SendDataToMeter(sendData, ref bytData)) return false;

            if (!MeterCommProtocol.ExplainDataByUserControl(bytData, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = string.Format("解析电能表返回报文出错,{0}", errString);
                return false;
            }
            meterInfo.CurrentDesc = string.Format("电能表的{0}控制操作成功", uc);
#endif
            return true;
        }
        #endregion

        #region --------------------一,二类参数设置------------------
        /// <summary>
        /// 一类二类参数设置
        /// </summary>
        /// <param name="sendData">发送数据的回调函数</param>
        /// <param name="getParameter">从加密机获取密文的回调函数</param>
        /// <param name="bsCode">一类参数标识码</param>
        /// <param name="paramValue">参数值</param>
        /// <param name="isOneClass">是否是一类参数</param>
        /// <returns></returns>
        public bool SetClassParam(Func_SendDataToSet sendData, Func_IdentityAuthentication Identity, Func_GetParameterUpdate getParameter, Func_GetParameterElseUpdate getParameterElse, string bsCode, string paramValue, bool isOneClass)
        {
            if (!ReadMeterAddrAndID(sendData)) return false;

            if (this.rand == "" || this.esamSerial == "" || this.IdentityNum >= 3)  //判断认证后可使用次数，如果大于3次则重新认证。
            {
                this.IdentityNum = 0;
                if (!getMeterESAM(sendData, Identity)) return false;
            }

            this.IdentityNum++;

            EnumKeyState ks = isPrivateKey ? EnumKeyState.私钥状态 : EnumKeyState.公钥状态;

            string putData = isOneClass ? paramValue.Replace("|", "") : string.Format("{0}{1}", bsCode, paramValue);           //一类参数不带标示符

            string apdu = getApdu(bsCode, putData.Length, isOneClass);

            string outMac; string outData;

            meterInfo.CurrentDesc = string.Format("从加密机获取{0}参数设置MAC", isOneClass ? "一类" : "二类");
#if !DEBUG
            bool result = false;
            if (isOneClass)
            {
                result = getParameter(ks, this.rand, this.meterID, this.esamSerial, apdu, putData, out outMac);
                outData = paramValue;
                if (outMac.Length > 8) outMac = outMac.Substring(8);
            }
            else
            {
                result = getParameterElse(ks, this.rand, this.meterID, this.esamSerial, apdu, putData, out outData, out outMac);
            }
            if (!result)
            {
                meterInfo.CurrentDesc = string.Format("获取{1}:{0}密文失败", bsCode, isOneClass ? "一类参数" : "二类参数");
                return false;
            }
#else
            System.Threading.Thread.Sleep(500);
#endif
            meterInfo.CurrentDesc = string.Format("正在写电能表参数");

#if !DEBUG
            string BwCode; string errString;

            byte[] bytSend = MeterCommProtocol.WeaveDataByParameterUpdate(meterInfo.MeterAddress, isOneClass, bsCode, outData, outMac, out BwCode);

            if (!SendDataToMeter(sendData, ref bytSend)) return false;

            if (!MeterCommProtocol.ExplainDataByParameterElseUpdate(bytSend, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "解析电能表返回帧出错，" + errString;
                return false;
            }
#else
            System.Threading.Thread.Sleep(1000);
#endif
            meterInfo.CurrentDesc = string.Format("写电能表参数成功");
            return true;
        }

        #endregion

        #region -----------------基本参数设置、操作-----------------

        public bool SetStandardParam(Func_SendDataToSet sendData, string pwd, string level, string BsCode, string paramValue)
        {
            if (!ReadMeterAddrAndID(sendData)) return false;

            meterInfo.CurrentDesc = string.Format("写数据项：{0}", BsCode);
#if DEBUG
            System.Threading.Thread.Sleep(1000);
#else

            string BwCode; string errString;
            byte[] bytData = MeterCommProtocol.WeaveDataByStandardUpdate(meterInfo.MeterAddress, pwd, level, BsCode, paramValue, out BwCode);

            if (!SendDataToMeter(sendData, ref bytData))
            {
                meterInfo.CurrentDesc = string.Format("写基本数据项失败,无响应");
                return false;
            }

            if (!MeterCommProtocol.ExplainDataByStandardUpdate(bytData, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = string.Format("返回数据帧失败，{1}", errString);
                return false;
            }
#endif
            meterInfo.CurrentDesc = string.Format("写基本数据项成功");

            return true;
        }

        #endregion

        #region -------------------------参数读取操作、复核使用-----------------
        /// <summary>
        /// 复核设置电能表参数（复核成功返回TRUE，失败返回FALSE)
        /// </summary>
        /// <param name="sendData"></param>
        /// <param name="BsCode"></param>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public bool ReviewParam(Func_SendDataToSet sendData, string BsCode, string paramValue)
        {
            if (!ReadMeterAddrAndID(sendData)) return false;

            meterInfo.CurrentDesc = string.Format("读取参数项：{0}", BsCode);

#if DEBUG
            System.Threading.Thread.Sleep(500);
#else
            string BwCode; string errString; string returnData;

            byte[] bytData = MeterCommProtocol.WeaveDataByGetMeterParam(meterInfo.MeterAddress, BsCode, out BwCode);

            if (!SendDataToMeter(sendData, ref bytData)) return false;

            if (!MeterCommProtocol.ExpalinDataByGetMeterParam(bytData, out returnData, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = string.Format("返回帧出错：{1}", BsCode, errString);
                return false;
            }

            returnData = returnData.Replace(BsCode, "");

            if (paramValue.IndexOf('|', 0) < 0)     //表示为单一数据
            {
                if (paramValue == returnData)
                {
                    meterInfo.CurrentDesc = string.Format("复核成功");
                    return true;
                }
                else
                {
                    meterInfo.CurrentDesc = string.Format("复核失败W:{0}M：{1}", paramValue, returnData);
                    return false;
                }
            }
            else
            {
                string[] paramItems = paramValue.Split('|');
                int len = paramItems[0].Length;
                if (len == 0) return true;

                List<string> returnItems = new List<string>();

                for (int i = 0; i < returnData.Length / len; i++)
                {
                    string tmp = returnData.Substring(i * len, len);
                    if (tmp == "".PadLeft(len, '0') || tmp == "".PadLeft(len, 'F')) continue;
                    returnItems.Add(returnData.Substring(i * len, len));
                }

                if (paramItems.Length != returnItems.Count)
                {
                    meterInfo.CurrentDesc = string.Format("复核失败W:{0}M：{1}", paramValue, returnData);
                    return false;
                }

                for (int i = 0; i < paramItems.Length; i++)
                {
                    int j = 0;
                    do
                    {
                        if (paramItems[i] == returnItems[j]) break;
                        j++;
                    } while (j < returnItems.Count);
                    if (j < returnItems.Count)
                    {
                        continue;
                    }
                    else
                    {
                        meterInfo.CurrentDesc = string.Format("复核失败W:{0}M：{1}", paramValue, returnData);
                        return false;
                    }
                }
            }
#endif
            meterInfo.CurrentDesc = string.Format("复核成功", BsCode);
            return true;
        }

        #endregion

        #endregion

        #region -------------------读表时间-------------------

        private bool getMeterTime(Func_SendDataToSet sendData, out DateTime meterTime)
        {
            meterTime = DateTime.Now;

            string BwCode; string paramValue; string errString;

            if (!ReadMeterAddrAndID(sendData)) return false;

            meterInfo.CurrentDesc = "正在读取电能表当前日期";

            byte[] bytData = MeterCommProtocol.WeaveDataByGetMeterParam(this.meterInfo.MeterAddress, "04000101", out BwCode);

            if (!SendDataToMeter(sendData, ref bytData)) return false;

            if (!MeterCommProtocol.ExpalinDataByGetMeterParam(bytData, out paramValue, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "解析表返回报文出错，" + errString;
                return false;
            }
            if (paramValue.Length != 16)
            {
                meterInfo.CurrentDesc = "电能表返回数据不正确";
                return false;
            }
            meterTime = new DateTime(int.Parse(paramValue.Substring(0, 2)), int.Parse(paramValue.Substring(2, 2)), int.Parse(paramValue.Substring(4, 2)));

            meterInfo.CurrentDesc = "正在读取电能表当前时间";

            bytData = MeterCommProtocol.WeaveDataByGetMeterParam(this.meterInfo.MeterAddress, "04000102", out BwCode);

            if (!SendDataToMeter(sendData, ref bytData)) return false;

            if (!MeterCommProtocol.ExpalinDataByGetMeterParam(bytData, out paramValue, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "解析表返回报文出错，" + errString;
                return false;
            }
            if (paramValue.Length != 14)
            {
                meterInfo.CurrentDesc = "电能表返回数据不正确";
                return false;
            }

            meterTime.Add(new TimeSpan(int.Parse(paramValue.Substring(0, 2)), int.Parse(paramValue.Substring(2, 2)), int.Parse(paramValue.Substring(4, 2))));
            meterInfo.CurrentDesc = "电能表当前时间读取成功";
            return true;
        }


        #endregion

        #region ------------------身份认证---------------------

        /// <summary>
        /// 获取ESAM序列号
        /// </summary>
        /// <param name="sendData">发送数据回调函数</param>
        /// <param name="Identity">加密机访问回调函数</param>
        /// <param name="isPrivateKey">是否是私钥</param>
        /// <returns></returns>
        private bool getMeterESAM(Func_SendDataToSet sendData, Func_IdentityAuthentication Identity)
        {
            isPrivateKey = false;
            if (Communicate.IdentityType == EnumIdentityType.自动认证)
            {
                if (!getMeterESAM(sendData, Identity, EnumKeyState.公钥状态))
                {
                    if (!getMeterESAM(sendData, Identity, EnumKeyState.私钥状态)) return false;
                    isPrivateKey = true;
                }
            }
            else if (Communicate.IdentityType == EnumIdentityType.仅公钥下认证)
            {
                return getMeterESAM(sendData, Identity, EnumKeyState.公钥状态);
            }
            else if (Communicate.IdentityType == EnumIdentityType.仅私钥下认证)
            {
                if (!getMeterESAM(sendData, Identity, EnumKeyState.私钥状态)) return false;
                isPrivateKey = true;
            }
            return true;

        }
        /// <summary>
        /// 读取电能表ESAM序列号
        /// </summary>
        /// <param name="sendData"></param>
        /// <param name="Identity"></param>
        /// <param name="ks"></param>
        /// <returns></returns>
        private bool getMeterESAM(Func_SendDataToSet sendData, Func_IdentityAuthentication Identity, EnumKeyState ks)
        {
            rand = ""; esamSerial = "";

            string out_Endata;

            meterInfo.CurrentDesc = string.Format("正在进行{0}远程身份认证", ks);

#if DEBUG
            System.Threading.Thread.Sleep(2000);
            rand = "11111111"; esamSerial = "".PadLeft(16, '0');
#else
            if (!Identity(ks, meterID, out rand, out out_Endata))
            {
                meterInfo.CurrentDesc = string.Format("{0}远程身份认证失败，加密机无响应", ks);
                return false;
            }
#endif
            meterInfo.CurrentDesc = "获取ESAM序列号";
#if DEBUG
            System.Threading.Thread.Sleep(500);
#else
            string yz = ks == EnumKeyState.公钥状态 ? "1" : meterID;

            string BwCode; string errString;

            byte[] BytData = MeterCommProtocol.WeaveDataByIdentity(meterInfo.MeterAddress, yz, rand, out_Endata, out BwCode);

            if (!SendDataToMeter(sendData, ref BytData)) return false;

            if (!MeterCommProtocol.ExplainDataByIdentity(BytData, ref rand, ref esamSerial, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "读取ESAM序列号失败:" + errString;
                return false;
            }
#endif
            meterInfo.CurrentDesc = "读取ESAM序列号成功";
            return true;

        }

        #endregion

        #region ---------------------状态检查 状态清零----------------------

        private bool CheckMeterStateInfo(Func_SendDataToSet sendData, Func_GetClearKey getClearKey, Func_IdentityAuthentication Identity)
        {
            string BwCode;
            string errString;
            string state = "";

            meterInfo.CurrentDesc = "开始检查电能表状态";

#if DEBUG
            System.Threading.Thread.Sleep(1000);
#else
            byte[] BytData = MeterCommProtocol.WeaveDataByState(meterInfo.MeterAddress, out BwCode);

            if (!SendDataToMeter(sendData, ref BytData)) return false;

            if (!MeterCommProtocol.ExplainDataByState(BytData, ref state, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "读取电能表状态出错：" + errString;
                return false;
            }

            if (state != "00000000")      //如果状态不等于00000000就需要清理状态
            {
                if (!this.isPrivateKey)
                {
                    if (!ClearMeterState(EnumKeyState.公钥状态, sendData, getClearKey))       //如果在公钥状态下，进行公钥下清零失败,则需要在公钥下私钥指令清零
                    {
                        if (!getMeterESAM(sendData, Identity, EnumKeyState.公钥状态))
                        {
                            meterInfo.CurrentDesc = "公钥下身份认证失败";
                            return false;
                        }

                        if (!ClearMeterState(EnumKeyState.私钥状态, sendData, getClearKey))           //在公钥状态下，使用私钥指令进行清零操作
                        {
                            meterInfo.CurrentDesc = "公钥认证信息转为私钥清零失败..";
                            return false;
                        }
                    }
                }
                else
                {
                    if (!ClearMeterState(EnumKeyState.私钥状态, sendData, getClearKey))       //私钥状态，在私钥下清零
                    {
                        meterInfo.CurrentDesc = "私钥下身份认证失败";
                        return false;
                    }
                    this.isClear = true;            //在私钥下如果清零OK，表示本地费控表不需要再次插卡。
                }
            }
#endif
            meterInfo.CurrentDesc = "状态检查完毕";
            return true;
        }

        /// <summary>
        /// 清理电能表状态
        /// </summary>
        /// <param name="ks">密钥状态</param>
        /// <param name="sendData">发送数据回调函数</param>
        /// <param name="getClearKey">获取清理密钥回调函数</param>
        /// <returns></returns>
        private bool ClearMeterState(EnumKeyState ks, Func_SendDataToSet sendData, Func_GetClearKey getClearKey)
        {
            string outKey; string outMac; string BwCode; string errString;

            if (!getClearKey(ks, this.meterID, this.rand, this.esamSerial, "00000000", out outKey, out outMac))
            {
                meterInfo.CurrentDesc = string.Format("从加密机中获取{0}清零密文失败", ks);
            }
            meterInfo.CurrentDesc = string.Format("从加密机中获取【{0}】状态清零密文成功", ks);

            byte[] bytData = MeterCommProtocol.WeaveDataByClearKey(meterInfo.MeterAddress, outKey, outMac, out BwCode);

            meterInfo.CurrentDesc = "正在进行电能表清零操作";

            if (!SendDataToMeter(sendData, ref  bytData)) return false;

            if (!MeterCommProtocol.ExplainDataByClearKey(bytData, out BwCode, out errString))
            {
                meterInfo.CurrentDesc = "电能表清零操作失败：" + errString;
                return false;
            }

            meterInfo.CurrentDesc = "电能表清零操作完成";

            return true;
        }

        #endregion

        #region  ----------------------密钥下装--------------------

        private bool DownKeyToMeter(Func_SendDataToSet sendData, Func_IdentityAuthentication Identity, Func_GetClearKey getClearKey, Func_GetUpdateKey getUpdateKey)
        {
            for (int rety = 0; rety < 5; rety++)
            {
                if (!getMeterESAM(sendData, Identity)) return false;

                if (rety > 0)
                {
                    if (!CheckMeterStateInfo(sendData, getClearKey, Identity))
                    {
                        meterInfo.CurrentDesc = string.Format("第{0}次密钥下装失败后重试清表状态失败", rety);
                        return false;
                    }

                    if (!getMeterESAM(sendData, Identity))
                    {
                        meterInfo.CurrentDesc = "状态清零后重新身份认证失败";
                        return false;
                    }
                }

                meterInfo.CurrentDesc = "正在从加密机获取电能表下装密钥信息";

                Dictionary<EnumKeyNo, string> keyList = new Dictionary<EnumKeyNo, string>();
#if !DEBUG
                if (!getUpdateKey(this.meterID, this.rand, this.esamSerial, out keyList))
                {
                    meterInfo.CurrentDesc = "从加密机获取下装密钥信息失败";
                    return false;
                }
#else
                System.Threading.Thread.Sleep(1000);
#endif
                if (DownKey(sendData, keyList)) return true;

                meterInfo.CurrentDesc = string.Format("第（{0}）次，密钥下装失败", rety);
            }

            return false;
        }
        /// <summary>
        /// 密钥下装操作
        /// </summary>
        /// <param name="sendData"></param>
        /// <param name="keyList"></param>
        /// <returns></returns>
        private bool DownKey(Func_SendDataToSet sendData, Dictionary<EnumKeyNo, string> keyList)
        {
            for (int i = 0; i < 4; i++)
            {
                EnumKeyNo tmpKeyNo = i == 0 ? EnumKeyNo.主控密钥 : (EnumKeyNo)i;

                if (i == 0 && !IsYuanCheng) continue;       //如果不是远程，且I==0的时候跳过，不下装主控密钥
#if !DEBUG
                if (!keyList.ContainsKey(tmpKeyNo)) continue;

                string[] data = keyList[tmpKeyNo].Split('|');//数据部分结构为 KeyData|KeyInfo|KeyMac(密文|密钥信息|下装MAC

                string BwCode; string errString;
#endif
                meterInfo.CurrentDesc = string.Format("进行{0}的下装操作", tmpKeyNo);

#if !DEBUG
                byte[] bytData = MeterCommProtocol.WeaveDataByUpdateKey(tmpKeyNo, meterInfo.MeterAddress, data[1], data[2], data[0], out BwCode);

                if (!SendDataToMeter(sendData, ref bytData)) return false;

                if (!MeterCommProtocol.ExplainDataByUpdateKey(tmpKeyNo, bytData, out BwCode, out errString))
                {
                    meterInfo.CurrentDesc = "解析密钥下装电能表返回数据报文出错，" + errString;
                    return false;
                }
#else
                System.Threading.Thread.Sleep(1000);
#endif
                meterInfo.CurrentDesc = string.Format("{0}下装成功", tmpKeyNo);
            }

            return true;
        }

        #endregion

        #region -------------------配套辅助函数----------------------

        /// <summary>
        /// 获取APDU
        /// </summary>
        /// <param name="BsCode"></param>
        /// <param name="paramLenght"></param>
        /// <param name="isOneClass"></param>
        /// <returns></returns>
        private string getApdu(string BsCode, int paramLenght, bool isOneClass)
        {
            if (paramLenght % 2 != 0) paramLenght += 1;
            paramLenght = paramLenght / 2;    //除以2是将字符长度转化为字节长度，2个字符HEX表示一个1字节
            string apdu = BsCode.Substring(2, 2);//di2
            int offset = 0;

            if (isOneClass)
            {
                apdu = "82";
                if (BsCode.Substring(2, 2) == "05")   //0405XXXX是费率文件
                {
                    if (BsCode.Substring(4, 2) == "01")     //0040501XX是一套费率文件
                    {
                        apdu = "83";
                    }
                    else if (BsCode.Substring(4, 2) == "02") //0040502XX是二套费率文件
                    {
                        apdu = "84";
                    }
                }

                if (apdu == "00")
                {
                    switch (BsCode.Substring(4, 2))
                    {
                        case "01":      //两套费率电价切换时间04000108
                            offset = 10;
                            break;
                        case "03":          //电流、电压互感器变比 04 00 03 06/07;
                            offset = 24 + (int.Parse(BsCode.Substring(6, 2)) - 6) * 3;
                            break;
                        case "10":  //报警金额 04 00 10 01/02
                            offset = 16 + (int.Parse(BsCode.Substring(6, 2)) - 1) * 4;
                            break;
                    }
                }
                else
                {
                    int feilvhao = int.Parse(BsCode.Substring(6, 2), System.Globalization.NumberStyles.AllowHexSpecifier);

                    offset = 4 * feilvhao;
                }
            }
            else
            {
                apdu = (89 + int.Parse(apdu) % 5).ToString();
            }

            int len = isOneClass ? paramLenght + 4 : (8 * (int)(paramLenght / 8) + (paramLenght % 8 == 0 ? 0 : 8)) + 8;   //一类参数的LC=数据长度+4个字节MAC长度，二类参数的LC=数据长度不足8个字节整数倍，则补足+4个字节MAC+4个字节DI

            return string.Format("04d6{0}{1}{2:x2}", apdu, offset.ToString("x2"), len);
        }


        /// <summary>
        /// 读取电能表通信地址及表号
        /// </summary>
        /// <param name="sendData"></param>
        /// <returns></returns>
        private bool ReadMeterAddrAndID(Func_SendDataToSet sendData)
        {
            if (!isReadID)
            {
                if (meterInfo.MeterAddress == "" || meterInfo.MeterAddress == "".PadLeft(12, 'A'))
                {
                    if (!ReadMeterAddr(sendData)) return false;
                }
                if (!ReadMeterID(sendData)) return false;
            }
            return true;
        }
        /// <summary>
        /// 发送命令帧到表，自动重发3次
        /// </summary>
        /// <param name="sendData"></param>
        /// <param name="BytData"></param>
        /// <returns></returns>
        private bool SendDataToMeter(Func_SendDataToSet sendData, ref byte[] BytData)
        {
            string meterCurrentDesc = meterInfo.CurrentDesc;

            for (int i = 0; i < CONST_RECOUNT; i++)
            {
                byte[] tmpSend = BytData;
                if (i == 0 && meterInfo.MeterAddress == "".PadLeft(12, 'A')) WriteDebugTxt(BitConverter.ToString(tmpSend).Replace("-", " "), true, true);

                if (i > 0)
                {
                    meterInfo.CurrentDesc = meterCurrentDesc + string.Format("重试【{0}】", i);
                }

                if (meterInfo.MeterAddress != "".PadLeft(12, 'A'))
                {
                    WriteDebugTxt(string.Format("{0}{1}", i > 0 ? "【重试】" : "", BitConverter.ToString(tmpSend).Replace("-", " ")), true, false);
                }
                Console.WriteLine(string.Format("第{0}表位,发送数据", meterInfo.MeterBwh));
                if (sendData(ref tmpSend, true, meterInfo.MeterBwh))
                {
                    WriteDebugTxt(BitConverter.ToString(tmpSend).Replace("-", " "), false, false);
                    BytData = tmpSend;
                    return true;
                }
                WriteDebugTxt("发送信息后电能表无响应", false, false);
            }

            meterInfo.CurrentDesc = meterCurrentDesc + "电能表无响应";

            return false;
        }

        ///// <summary>
        ///// 延迟
        ///// </summary>
        ///// <param name="delayTime"></param>
        ///// <param name="spacing"></param>
        //private void Delay(float delayTime, int spacing)
        //{
        //    DateTime dt = DateTime.Now;

        //    while (DateTime.Now.Subtract(dt).TotalMilliseconds < delayTime)
        //    {
        //        System.Threading.Thread.Sleep(spacing);
        //    }
        //}
        /// <summary>
        /// 写报文文本
        /// </summary>
        /// <param name="MessageString">文本消息</param>
        /// <param name="IsSend">是否是发送</param>
        /// <param name="IsNew">是不是创建新文档</param>
        private void WriteDebugTxt(string MessageString, bool IsSend, bool IsNew)
        {
            WriteDebugTxt(MessageString, meterInfo.MeterBwh.ToString("D2"), IsSend, IsNew);
        }

        /// <summary>
        /// 写报文文本(读表地址的时候专用)
        /// </summary>
        /// <param name="MessageString">文本消息</param>
        /// <param name="meterAdr">表通信地址</param>
        /// <param name="IsSend">是否发送</param>
        /// <param name="IsNew">是不是新创建文档</param>
        private void WriteDebugTxt(string MessageString, string meterAdr, bool IsSend, bool IsNew)
        {
            if (!m_IsPrintDebug) return;

            string dirString = string.Format(@"{0}\{1}", System.Environment.CurrentDirectory, CONST_DEBUGPATH);

            if (IsNew)
            {
                System.IO.File.Create(string.Format(@"{0}\[645]{1}.log", dirString, meterAdr)).Close();
            }

            System.IO.File.AppendAllText(string.Format(@"{0}\[645]{1}.log", dirString, meterAdr)
                                         , string.Format("{0}{1}\r\n", IsSend ? "发送--->" : "返回<---", MessageString), Encoding.Default);
        }


        #endregion
    }
}
